﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.ComponentModel;

namespace InductBase
{
    [TypeConverter(typeof(InductBase.Helpers.BasicTypesConverter))]
    public class Transformation
    {
        public Transformation AdditionalTransformation;

        public Vec2 Position;
        public Vec2 Scale;
        public Vec2 Shift;
        public double Rotation;

        public Transformation()
        {
            Position = Vec2.Null;
            Scale = new Vec2(1, 1);
            Shift = Vec2.Null;
            Rotation = 0;
        }

        public Transformation(Vec2 position, Vec2 scale, Vec2 shift, double rotation = 0)
        {
            Position = position;
            Scale = scale;
            Shift = shift;
            Rotation = rotation;
        }

        public Transformation Copy()
        {
            return new Transformation(this);
        }

        public Transformation(Transformation transformation)
        {
            Position = transformation.Position;
            Scale = transformation.Scale;
            Shift = transformation.Shift;
            Rotation = transformation.Rotation;
            AdditionalTransformation = transformation.AdditionalTransformation;
        }

        static public Transformation LinearTween(Transformation a, Transformation b, Double part)
        {
            double arot = a.Rotation;
            double brot = b.Rotation;
            double delta = brot - arot;
            if (delta > 180) brot -= 360;
            if (delta < -180) brot += 360;


            return new Transformation(
                Vec2.LinearTween(a.Position, b.Position, part),
                Vec2.LinearTween(a.Scale, b.Scale, part),
                Vec2.LinearTween(a.Shift, b.Shift, part),
                arot + part * (brot - arot)
                );
        }

        public Transformation SetPosition(Vec2 vec)
        {
            Position = vec;
            return this;
        }

        public Transformation Move(Vec2 vec)
        {
            Position += vec;
            return this;
        }

        public Transformation Rotate(double angle)
        {
            Rotation += angle;
            return this;
        }

        public Transformation ScaleUp(Vec2 scale)
        {
            Scale *= scale;
            return this;
        }

        public Transformation ShiftUp(Vec2 vec)
        {
            Shift += vec;
            return this;
        }

        static Transformation()
        {
            
        }

        public static Transformation Default
        {
            get
            {
                return new Transformation(Vec2.Null, new Vec2(1, 1), Vec2.Null, 0);
            }
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder("", 16);
            sb.Append(Position.ToString());
            sb.Append('_');
            sb.Append(Scale.ToString());
            sb.Append('_');
            sb.Append(Shift.ToString());
            sb.Append('_');
            sb.Append(Rotation.ToString().Replace(',', '.'));
            return sb.ToString();
        }

        public static Transformation Parse(string input)
        {
            string[] args = input.Split('_');

            Transformation transf = new Transformation();
            transf.Position = Vec2.Parse(args[0]);
            transf.Scale = Vec2.Parse(args[1]);
            transf.Shift = Vec2.Parse(args[2]);
            transf.Rotation = Double.Parse(args[3], CultureInfo.InvariantCulture);

            return transf;
        }

        public Vec2 FromTransformation(Vec2 point)
        {
            Vec2 result = point.Copy();

            if (AdditionalTransformation != null)
                result = AdditionalTransformation.FromTransformation(result);

            result += Position;
            result = result.Rotate(Rotation);
            result *= Scale;
            result += Shift;
            return result;
        }

        public Vec2 ToTransformation(Vec2 point)
        {
            Vec2 result = point.Copy();
            result += Shift;
            result *= Scale;
            result = result.Rotate(Rotation);
            result += Position;

            if (AdditionalTransformation != null)
                result = AdditionalTransformation.ToTransformation(result);
            return result;
        }

        public Transformation AddTransformation(Transformation transformation)
        {
            Transformation result = this.Copy();
            result.AdditionalTransformation = transformation;
            return result;
        }

        public static bool operator ==(Transformation a, Transformation b)
        {
            if (ReferenceEquals(a, b)) return true;
            if (ReferenceEquals(a, null) && ReferenceEquals(b, null)) return true;
            if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) return false;

            if (
                (a.Position == b.Position)
                && (a.Scale == b.Scale)
                && (a.Rotation == b.Rotation)
                && (a.Shift == b.Shift)
                )
            {
                return true;
            }

            return false;
        }

        public static bool operator !=(Transformation a, Transformation b)
        {
            return !(a == b);
        }

        public override bool Equals(object obj)
        {
            return this == obj;
        }
    }
}
